//#define TIXML_USE_STL
#define BOOST_DISABLE_THREADS
#ifdef WIN32
#include <Tinyxml\tinyxml.h>
#else
#include "tinyxml.h"
#endif
#include <cmath>
#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <fstream>
#include <string>
#include <boost/variant.hpp>
#include <boost/variant/get.hpp>
#include <vector>
#include <deque>
#include <utility>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include "chipmunk_object.h"
#include "totem_obj_config.h"
#define _USE_MATH_DEFINES
#include <math.h>
#include <iomanip>
#include "game_logic.h"
#include "utils.h"
//#ifndef WIN32
//#undef check
//#endif
//#include <boost/lambda/lambda.hpp>

template<class Archive>
void serialize(Archive & ar, cpVect &obj, const unsigned int version)
{
	ar  & boost::serialization::make_nvp("x", obj.x)
		& boost::serialization::make_nvp("y", obj.y);
}

template<class Archive>
void serialize(Archive & ar, object_base_info_t &obj, const unsigned int version)
{
	ar  & boost::serialization::make_nvp("density", obj.density)
		& boost::serialization::make_nvp("friction", obj.friction)
		& boost::serialization::make_nvp("elasticity", obj.elasticity)
		& boost::serialization::make_nvp("force", obj.force);
		
	//ar & BOOST_SERIALIZATION_NVP(obj.density);
	//ar & BOOST_SERIALIZATION_NVP(obj.friction);
	//ar & BOOST_SERIALIZATION_NVP(obj.elasticity);
}

using boost::variant;

typedef variant< const char*, int > t_var;

using namespace std;

vector< map_info_t > g_maps;

cpVect g_gravity = {0, -290};
object_base_info_t g_floor_info = {0.15f, 0.2f, 0.8f, {0, 0}};
object_base_info_t g_man_info = {0.2f, 0.5f, 0.1f, {0, 0}};
object_base_info_t g_iron_info = {1.0f, 0.5f, 0.1f, {0, 0}};
object_base_info_t g_wood_info = {0.2f, 0.6f, 0.2f, {0, 0}};
object_base_info_t g_rubber_info = {0.15f, 0.2f, 0.8f, {0, 0}};
object_base_info_t g_hydrogen_info = {0.15f, 0.2f, 0.8f, {0, 60}};
float_t g_explosive_range = 60;
float_t g_explosive_amplitude = 600;

namespace read_config{
	using namespace boost;

	void init()
	{
		//			using namespace std;
		//			for (int i = 1; i <= 200; i++) {
		//				cout << "{@\"" << setw(2) << setfill('0') << i << ".png\", @\"";
		//				cout << setw(2) << setfill('0') << i << ".png\", @\"";
		//				cout << setw(2) << setfill('0') << i << ".png\"}," << endl;
		//			}
		
		string filename(getResPath("config.xml"));
		//			std::ofstream ofs(filename.c_str());
		//			boost::archive::xml_oarchive ia(ofs);			
		std::ifstream ifs(filename.c_str());
		boost::archive::xml_iarchive ia(ifs);
		//			string filename(getResPath("config.b"));
		//			std::ifstream ofs(filename.c_str());
		//			boost::archive::binary_iarchive ia(ofs);
		ia & BOOST_SERIALIZATION_NVP(g_gravity);
		ia & BOOST_SERIALIZATION_NVP(g_floor_info);
		ia & BOOST_SERIALIZATION_NVP(g_man_info);
		ia & BOOST_SERIALIZATION_NVP(g_iron_info);
		ia & BOOST_SERIALIZATION_NVP(g_wood_info);
		ia & BOOST_SERIALIZATION_NVP(g_rubber_info);
		ia & BOOST_SERIALIZATION_NVP(g_hydrogen_info);
		ia & BOOST_SERIALIZATION_NVP(g_explosive_range);
		ia & BOOST_SERIALIZATION_NVP(g_explosive_amplitude);
		//			{
		//				string filename("config.b");
		//				std::ofstream ofs(filename.c_str());
		//				boost::archive::binary_oarchive ia(ofs);
		//				ia & BOOST_SERIALIZATION_NVP(g_gravity);
		//				ia & BOOST_SERIALIZATION_NVP(g_floor_info);
		//				ia & BOOST_SERIALIZATION_NVP(g_man_info);
		//				ia & BOOST_SERIALIZATION_NVP(g_iron_info);
		//				ia & BOOST_SERIALIZATION_NVP(g_wood_info);
		//				ia & BOOST_SERIALIZATION_NVP(g_rubber_info);
		//				ia & BOOST_SERIALIZATION_NVP(g_hydrogen_info);
		//				ia & BOOST_SERIALIZATION_NVP(g_explosive_range);
		//				ia & BOOST_SERIALIZATION_NVP(g_explosive_amplitude);
		//			}
	}
	
};


namespace maps{
	typedef object_con_t::const_iterator map_iterator_t;
	void getMaps(int index, map_iterator_t &iter, map_iterator_t &end)
	{
		ASSERT(0 != g_maps.size());
		ASSERT(index < g_maps.size());
		ASSERT(0 != g_maps[index].objs.size());
		iter = g_maps[index].objs.begin();
		end = g_maps[index].objs.end();
	}
	
	map_info_t &getMapinfo(int index)
	{
		ASSERT(index < g_maps.size());
		return g_maps[index];
	}
};



void read_map_xml()
{
	vector<string> image_names;
	vector<pair<int, cpVect> > tiles;
	image_names.reserve(0x10);
#ifdef WIN32
	TiXmlDocument doc( "map.xml");
#else
	//getNsAutoreleasePoolInstance();
#ifndef FREE_VERSION
	string _file_name = getResPath("map.xml");
#else
	string _file_name = getResPath("map_free.xml");
#endif	
	TiXmlDocument doc(_file_name.c_str());
#endif
	if (false ==doc.LoadFile()) {
		std::cout << "file loading false" << std::endl;
		return ;
	}
	TiXmlHandle docH( &doc );
	docH = docH.FirstChildElement( "MapInfo" );
	
	TiXmlElement* element = docH.FirstChildElement( "Images" ).Element();
	TiXmlElement *Image = element->FirstChildElement();
	while (NULL != Image) {
		//std::cout<< "   name = "<< Image->Attribute( "name" ) << std::endl;		
		image_names.push_back(string( Image->Attribute( "name" ) ));
		Image = Image->NextSiblingElement();
		//t_var tmp = Image->Attribute( "name" );
		//int i = boost::get<int>(tmp);
		//cout << i << endl;
	}
	element = docH.FirstChildElement( "Tiles" ).Element();
	TiXmlElement *tile = element->FirstChildElement();
	while (NULL != tile) {
		//std::cout<< "   name = "<< tile->Attribute( "ImgID" ) << std::endl;
		pair<int, cpVect> tile_info;
		tile_info.first = atoi(tile->Attribute( "ImgID" ));
		tile_info.second.x = atoi(tile->Attribute( "w" ));
		tile_info.second.y = atoi(tile->Attribute( "h" ));
		tiles.push_back(tile_info);
		tile = tile->NextSiblingElement();
	}

	g_maps.clear();
	element = docH.FirstChildElement( "Maps" ).Element();
	TiXmlElement *map = element->FirstChildElement();
	while(NULL != map) {		
		map_info_t map_info;		
		TiXmlElement *attrs = map->FirstChildElement( "Attributes" );
		TiXmlElement *attr =  attrs->FirstChildElement( "Attribute" );
		map_info.passSteps = atoi(attr->Attribute("Value"));
		attr = attr->NextSiblingElement();
		map_info.level = atoi(attr->Attribute("Value"));
		attr = attr->NextSiblingElement();
		map_info.bgImg = attr->Attribute("Value");		
		
		TiXmlElement *objs =  map->FirstChildElement( "Objects" );
		ASSERT( 0 != objs);
		TiXmlElement *object =  objs->FirstChildElement( "Object" );
		while(NULL != object) {
			object_t tmp = {0};

			tmp.pos.x = atoi(object->Attribute( "x" ));
			tmp.pos.y = atoi(object->Attribute( "y" ));
			tmp.texture_name = image_names[tiles[atoi(object->Attribute( "TileID" ))].first];
			if (string::npos != tmp.texture_name.find(STR_IRON)) {
				tmp.is_polygon = true;
				tmp.is_explosive = false;
				tmp.is_TNT = false;
				tmp = g_iron_info;
			}
			else if (string::npos != tmp.texture_name.find(STR_PLASTICS)) {
				tmp.is_polygon = true;
				tmp.is_explosive = true;
				tmp.is_TNT = false;
				tmp = g_rubber_info;
			}
			else if (string::npos != tmp.texture_name.find(STR_MAIN_ICON)) {
				tmp.is_polygon = true;
				tmp.is_explosive = false;
				tmp.is_TNT = false;
				tmp = g_man_info;
			}
			else if (string::npos != tmp.texture_name.find(STR_LIGHT_BOX)) {
				tmp.is_polygon = true;
				tmp.is_explosive = true;
				tmp.is_TNT = false;
				tmp = g_hydrogen_info;
			}
			else if (string::npos != tmp.texture_name.find(STR_TNT_BOX)) {
				tmp.is_polygon = true;
				tmp.is_explosive = true;
				tmp.is_TNT = true;
				tmp = g_wood_info;
			}
			else if (string::npos != tmp.texture_name.find(STR_MAIN_ICON)) {
				tmp.is_polygon = true;
				tmp.is_explosive = false;
				tmp.is_TNT = false;
				tmp = g_wood_info;
			}
			else {
				tmp.is_polygon = true;
				tmp.is_explosive = true;
				tmp.is_TNT = false;
				tmp = g_wood_info;
			}
			cpVect size;
			size = tiles[atoi(object->Attribute( "TileID" ))].second;
			int angle = atoi(object->Attribute( "angle" ));
			tmp.verts[0] = cpv(-size.x/2, -size.y/2);
			tmp.verts[1] = cpv(-size.x/2, size.y/2);
			tmp.verts[2] = cpv(size.x/2, size.y/2);
			tmp.verts[3] = cpv(size.x/2, -size.y/2);

			cpVect vect_angle = cpv(std::cos(M_PI * angle/ 180.0), std::sin(M_PI * angle/ 180.0));
			BOOST_FOREACH(cpVect &vt, tmp.verts) {
				vt = cpvunrotate(vt, vect_angle);
			}
			if (string::npos != tmp.texture_name.find(STR_MAIN_ICON)) {
				map_info.objs.push_front(tmp);
			}
			else {
				map_info.objs.push_back(tmp);
			}
			
			object = object->NextSiblingElement();
		}
		g_maps.push_back(map_info);
		map = map->NextSiblingElement();		
	}
	
	ASSERT(game_logic::totalLevel() == g_maps.size());
	game_logic::totalLevel() = g_maps.size();
}